#include "dvo.h"
#include <iostream>
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <future>

//pushflow elements
static void test_ps_receiver();
static void test_ps_transmitter();
static void test_ps_transceiver();
static void test_ps_joiner();
static void test_ps_detacher();
static void test_ps_selector();
static void test_ps_restrictor();
static void test_ps_separator();


//pullflow elements
static void test_pl_receiver();
static void test_pl_transmitter();
static void test_pl_transceiver();
static void test_pl_joiner();
static void test_pl_detacher();
static void test_pl_selector();
static void test_pl_restrictor();
static void test_pl_separator();

//adapters elements
static void test_accumulator();
static void test_relayer();


void elements_test()
{
  test_ps_receiver();
  test_ps_transmitter();
  test_ps_joiner();
  test_ps_detacher();
  test_ps_selector();
  test_ps_restrictor();
  test_ps_separator();
  
  std::cout <<std::endl;
  
  test_pl_receiver();
  test_pl_transmitter();
  test_pl_joiner();
  test_pl_detacher();
  test_pl_selector();
  test_pl_restrictor();
  test_pl_separator();

  std::cout <<std::endl;

  test_accumulator();
  test_relayer();

  std::cin.ignore();
}

class psReceiveTester : public dvo::ps::Receiver<int>
{
public:
  psReceiveTester() : result(0) {}
  int result;
protected:
  virtual void Process(int& value) override{
    result = std::move(value);
  }
};
void test_ps_receiver()
{
  psReceiveTester tester;
  int value = 6;
  if(tester.result != 0){ std::cout << "Test - 1: psReceiveTester is not ok " << std::endl; return; }
  tester.Input()->Transmit(value);
  if(tester.result != 6){ std::cout << "Test - 2: psReceiveTester is not ok" << std::endl; return; }
  std::cout << "Pushflow Receiver is ok" << std::endl;
}


class psTransmitterTest : public dvo::ps::Transmitter<int>
{
public:
  psTransmitterTest() {}
  virtual void Process() override{ int value = 6; Send(value); }
};

auto ps_socket = new dvo::ps::Socket<int>();
void test_ps_transmitter()
{
  
  psTransmitterTest tester;
  int value = 0; 
  ps_socket->Bind([&](int& forwarded) {value = std::move(forwarded);});
  tester.Output()->Connect(ps_socket);
  tester.Process();
  if(value != 6){ std::cout << "Test - 1: psTransmitterTest is not ok " << std::endl; return; }
  std::cout << "Pushflow Transmitter is ok" << std::endl;
}

class psTransceiverTest : public dvo::ps::Transceiver<int, int>
{
public:
  psTransceiverTest() {}
protected:
  virtual void Process(int& value) override{ Send(++value); }
};
void test_ps_transceiver()
{
  psTransceiverTest tester;
  int value = 0; 
  ps_socket->Bind([&](int& forwarded) {value = std::move(forwarded);});
  tester.Output()->Connect(ps_socket);
  tester.Input()->Transmit(value);
  if(value != 1){ std::cout << "Test - 1: psTransceiverTest is not ok " << std::endl; return; }
  std::cout << "Pushflow Transceiver is ok" << std::endl;
}

class psJoinerTester : public dvo::ps::Joiner<int, int, int>
{
public:
  psJoinerTester() { Add(1); Add(2); }
protected:
  virtual void Process() override{ int result = Get(1) + Get(2); Send(result);}
};
void test_ps_joiner()
{
  psJoinerTester tester;
  int value = 1; 
  std::promise<int> result;
  ps_socket->Bind([&](int& fwd) {result.set_value(std::move(fwd));});
  tester.Output()->Connect(ps_socket);
  auto fd1 = std::async(std::launch::async, [&]() {tester.Inputs(1)->Transmit(value);});
  auto fd2 = std::async(std::launch::async, [&]() {tester.Inputs(2)->Transmit(value);});
  fd1.get(); fd2.get(); value = result.get_future().get();
  if(value != 2){ std::cout << "Test - 1: psJoinerTester is not ok " << std::endl; return; }
  std::cout << "Pushflow Joiner is ok" << std::endl;
}

class psDetacherTester : public dvo::ps::Detacher<int, int, int>
{
public:
  psDetacherTester() { Add(1); Add(2); }
protected:
  virtual void Process(int& value) override{ v1_ = std::move(++value); Send(1, v1_); Send(2, ++value); }
private:
  int v1_;
};
void test_ps_detacher()
{
  psDetacherTester tester;
  int value = 1; 
  std::promise<int> pr1, pr2;
  ps_socket->Bind([&](int& fwd) 
  {
    int v = std::move(fwd); 
    pr1.set_value(v);
  });
  auto ps_socket2 = new dvo::ps::Socket<int>();
  ps_socket2->Bind([&](int& fwd) {int v = std::move(fwd); pr2.set_value(v);});
  tester.Outputs(1)->Connect(ps_socket);
  tester.Outputs(2)->Connect(ps_socket2);
  tester.Input()->Transmit(value);
  int v1 = pr1.get_future().get();
  int v2 = pr2.get_future().get();
  if(v1 != 2 || v2 != 3)
    { std::cout << "Test - 1: psDetacherTester is not ok " << std::endl; return; }
  std::cout << "Pushflow Detacher is ok" << std::endl;
}

class psSelectorTester : public dvo::ps::Selector<int, int, int>
{
public:
  psSelectorTester() { Add(1); Add(2); }
protected:
  virtual void Process(int& value) override{ Send(1, ++value); Send(2, ++value); }
};
void test_ps_selector()
{
  psSelectorTester tester;
  int value = 1; 
  std::promise<int> pr1, pr2;
  ps_socket->Bind([&](int& fwd) {pr1.set_value(std::move(fwd));});
  auto ps_socket2 = new dvo::ps::Socket<int>();
  ps_socket2->Bind([&](int& fwd) {pr2.set_value(std::move(fwd));});
  tester.Outputs(1)->Connect(ps_socket);
  tester.Outputs(2)->Connect(ps_socket2);
  tester.Input()->Transmit(value);
  if(pr1.get_future().get() != 2 || pr2.get_future().get() != 3)
    { std::cout << "Test - 1: psSelectorTester is not ok " << std::endl; return; }
  std::cout << "Pushflow Selector is ok" << std::endl;
}

class psRestrictorTester : public dvo::ps::Restrictor<int, int, int>
{
public:
  psRestrictorTester() { Add(1); Add(2); }
protected:
  virtual void Process(int key, int& value) override{ if(key != 2) return; Send(value);}
};
void test_ps_restrictor()
{
  psRestrictorTester tester;
  int value = 1;
  int result = 0;
  ps_socket->Bind([&](int& fwd) {result = std::move(fwd);});
  tester.Output()->Connect(ps_socket);
  tester.Inputs(1)->Transmit(value);
  if(result != 0)
    { std::cout << "Test - 1: psRestrictorTester is not ok " << std::endl; return; }
  tester.Inputs(2)->Transmit(value);
  if(result != 1)
    { std::cout << "Test - 2: psRestrictorTester is not ok " << std::endl; return; }
  std::cout << "Pushflow Restrictor is ok" << std::endl;
}

class psSeparatorHelper : public dvo::ps::Transceiver<int, int>
{
protected:
  virtual void Process(int& value) override{ Send(++value);}
};
void test_ps_separator()
{
  psSeparatorHelper helper1, helper2;
  dvo::ps::Separator<int> separator;
  *helper1.Output() >> separator.Input();
  *separator.Output() >> helper2.Input();
  *helper2.Output() >> ps_socket;
  int value = 0;
  int result = 0;
  std::promise<int> pr;
  *ps_socket >> std::function<void(int&)>([&](int& fwd) {pr.set_value(std::move(fwd));});
  value >> *helper1.Input();
  result = pr.get_future().get();
  if(result != 2)
    { std::cout << "Test - 1: Separator is not ok " << std::endl; return; }
  std::cout << "Pushflow Separator is ok" << std::endl;
}



//-------------------------------------------------------------------------------------------------------

dvo::pl::Socket<int> pl_socket;
class plReceiveTester : public dvo::pl::Receiver<int>
{
public:
  plReceiveTester() : result(0) {}
  int result;
  virtual void Process() override{
     result = Demand();
  }
};
void test_pl_receiver()
{
  plReceiveTester tester;
  pl_socket << std::function<int(void)>([&]()->int {return 1;});
  pl_socket << tester.Input();
  tester.Process();
  if(tester.result != 1){ std::cout << "Test - 1: plReceiveTester is not ok " << std::endl; return; }
  std::cout << "Pullflow Receiver is ok" << std::endl;
}


class plTransmitterTest : public dvo::pl::Transmitter<int>
{
public:
  plTransmitterTest() {}
protected:
  virtual int Process() override{ return 1; }
};
void test_pl_transmitter()
{
  plTransmitterTest tester;
  int result = tester.Output()->Request();
  if(result != 1){ std::cout << "Test - 1: plTransmitterTest is not ok " << std::endl; return; }
  std::cout << "Pullflow Transmitter is ok" << std::endl;
}

class plTransceiverTest : public dvo::pl::Transceiver<int, int>
{
public:
  plTransceiverTest() {}
protected:
  virtual int Process() override{ return Demand() + 1; }
};
void test_pl_transceiver()
{
  plTransceiverTest tester;
  int result;
  pl_socket << std::function<int(void)>([&]()->int {return 1;});
  pl_socket << tester.Input();
  //*tester.Output() << result;
  result << *tester.Output();
  if(result != 2){ std::cout << "Test - 1: plTransceiverTest is not ok " << std::endl; return; }
  std::cout << "Pullflow Transceiver is ok" << std::endl;
}


class plJoinerTester : public dvo::pl::Joiner<int, int, int>
{
public:
  plJoinerTester() { Add(1); Add(2); }
protected:
  virtual int Process() override { return Demand() + 1; }
};
void test_pl_joiner()
{
  plJoinerTester tester; 
  pl_socket << std::function<int(void)>([&]()->int {return 1;});
  pl_socket << tester.Input();
  auto fd1 = std::async(std::launch::async, [&]()->int {return tester.Outputs(1)->Request();});
  auto fd2 = std::async(std::launch::async, [&]()->int {return tester.Outputs(2)->Request();});
  int result = fd1.get() + fd2.get();
  if(result != 4){ std::cout << "Test - 1: plJoinerTester is not ok " << std::endl; return; }
  std::cout << "Pullflow Joiner is ok" << std::endl;
}

class plDetacherTester : public dvo::pl::Detacher<int, int, int>
{
public:
  plDetacherTester() { Add(1); Add(2); }
protected:
  virtual int Process() override{ int v1 =  Demand(1).get(); int v2 = Demand(2).get(); return v1 + v2;}
private:
  int v1_;
};
void test_pl_detacher()
{
  plDetacherTester tester;
  int result = 0;
  pl_socket << std::function<int(void)>([&]()->int {return 1;});
  pl_socket << tester.Inputs(1);
  pl_socket << tester.Inputs(2);
  //*tester.Output() << result;
  result << *tester.Output();
  if(result != 2)
    { std::cout << "Test - 1: plDetacherTester is not ok " << std::endl; return; }
  std::cout << "Pullflow Detacher is ok" << std::endl;
}

class plSelectorTester : public dvo::pl::Selector<int, int, int>
{
public:
  plSelectorTester() { Add(1); Add(2); }
protected:
  virtual int Process() override{ return Demand(1); }
};
void test_pl_selector()
{
  plSelectorTester tester;
  dvo::pl::Socket<int> pl_socket2;
  int result = 0;
  pl_socket << std::function<int(void)>([&]()->int {return 1;});
  pl_socket2 << std::function<int(void)>([&]()->int {return 2;});
  pl_socket << tester.Inputs(1);
  pl_socket2 << tester.Inputs(2);
  //*tester.Output() << result;
  result << *tester.Output();
  if(result != 1)
    { std::cout << "Test - 1: plSelectorTester is not ok " << std::endl; return; }
  std::cout << "Pullflow Selector is ok" << std::endl;
}

class plRestrictorTester : public dvo::pl::Restrictor<int, int, int>
{
public:
  plRestrictorTester() { Add(1); Add(2); }
protected:
  virtual int Process(int key) override{ if(key == 2) return Demand() + 1; return 0; }
};
void test_pl_restrictor()
{
  plRestrictorTester tester;
  int value = 1;
  int result1 = 0, result2 = 0;
  pl_socket << std::function<int(void)>([&]()->int {return 1;});
  pl_socket << tester.Input();
  //*tester.Outputs(1) << result1;
  //*tester.Outputs(2) << result2;
  result1 << *tester.Outputs(1);
  result2 << *tester.Outputs(2);
  if(result1 != 0 || result2 != 2)
    { std::cout << "Test - 1: plRestrictorTester is not ok " << std::endl; return; }
  std::cout << "Pushflow Restrictor is ok" << std::endl;
}

class plSeparatorHelper : public dvo::pl::Transceiver<int, int>
{
protected:
  virtual int Process() override{ return Demand() + 1;}
};
void test_pl_separator()
{
  plSeparatorHelper helper;
  dvo::pl::Separator<int> separator;
  std::promise<int> pr;
  int result;
  pr.set_value(2);
  pl_socket << std::function<int(void)>([&]()->int {return 1; pr.set_value(1);});
  pl_socket << separator.Input();
  *separator.Output() << helper.Input();
  //*helper.Output() << result;
  result << *helper.Output();
  pr.get_future().get();
  if(result != 2)
    { std::cout << "Test - 1: Separator is not ok " << std::endl; return; }
  std::cout << "Pushflow Separator is ok" << std::endl;
}

class psTransmitterHelper : public dvo::ps::Transmitter<int>
{
public:
  int value_;
  psTransmitterHelper() : value_(1) {}
  virtual void Process() override{ Send(value_); }
};
class plReceiverHelper : public dvo::pl::Receiver<int>
{
public:
  plReceiverHelper() : result(0) {}
  int result;
  virtual void Process() override{
     result = Demand();
  }
};
void test_accumulator()
{
  psTransmitterHelper helper1;
  plReceiverHelper helper2;
  dvo::Accumulator<int> tester;
  *helper1.Output() >> tester.Input();
  *tester.Output() << helper2.Input();
  helper1.Process();
  helper2.Process();
  if(helper1.value_ != helper2.result)
    { std::cout << "Test - 1: Accumulator is not ok " << std::endl; return; }
  std::cout << "Accumulator is ok" << std::endl;
}

class plTransmitterHelper : public dvo::pl::Transmitter<int>
{
public:
  plTransmitterHelper() {}
protected:
  virtual int Process() override{ return 1; }
};

class psReceiverHelper : public dvo::ps::Receiver<int>
{
public:
  int value_;
  psReceiverHelper() : value_(0) {}
protected:
  virtual void Process(int& value) override{ value_ = std::move(value); }
};

void test_relayer()
{
  plTransmitterHelper helper1;
  psReceiverHelper helper2;
  dvo::Relayer<int> tester;
  *helper1.Output() << tester.Input();
  *tester.Output() >> helper2.Input();
  tester.Process();
  if(helper2.value_ != 1)
    { std::cout << "Test - 1: Relayer is not ok " << std::endl; return; }
  std::cout << "Relayer is ok" << std::endl;
}

/*
*/